package com.poetize.plus.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.poetize.plus.entity.dto.ArticleDTO;
import com.poetize.plus.entity.dto.page.ArticlePageDTO;
import com.poetize.plus.entity.po.Article;
import com.poetize.plus.entity.po.Comment;
import com.poetize.plus.entity.vo.ArticleVO;
import com.poetize.plus.entity.vo.HomeArticleVO;
import com.poetize.plus.entity.vo.HomeRecommendArticleVO;
import com.poetize.plus.entity.vo.SelectVO;
import com.poetize.plus.entity.vo.page.ArticlePageVO;
import com.poetize.plus.mapper.*;
import com.poetize.plus.service.ArticleService;
import com.poetize.plus.utils.BaseConstant;
import com.poetize.plus.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author zuosy
 * @Date 2024/3/30 15:31
 **/
@Service
@RequiredArgsConstructor
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    private final CommentMapper commentMapper;

    private final RedisUtil redisUtil;

    @Override
    public Page<ArticlePageVO> articlePage(ArticlePageDTO articlePageDTO) {
        Page<Article> reqPage = Page.of(articlePageDTO.getCurrent(), articlePageDTO.getSize());
        return baseMapper.selectPage(reqPage, articlePageDTO);
    }

    @Override
    public Boolean addArticle(ArticleDTO articleDTO) {
        //加密时密码不能为空
        if (articleDTO.encoderStatus()) {
            if (!StringUtils.hasText(articleDTO.password())) {
                throw new IllegalArgumentException("密码不能为空");
            }
        }
        Article checkTitle = lambdaQuery()
                .eq(Article::getArticleTitle, articleDTO.articleTitle())
                .one();
        if (Objects.nonNull(checkTitle)) {
            throw new IllegalArgumentException("该文章已存在");
        }
        Article article = ArticleDTO.convertDTO(articleDTO);
        boolean insert = baseMapper.insert(article) > 0;
        if (!insert) {
            throw new IllegalArgumentException("数据异常");
        }
        //如果是推荐文章，则刷新首页推荐文章缓存
        if (article.getRecommendStatus()) {
            redisUtil.setHomeRedis(BaseConstant.Redis.HOME_RECOMMEND_ARTICLE, this::selectHomeRecommendArticle);
        }
        return true;
    }

    @Override
    public ArticleVO queryArticle(Long id) {
        Article article = baseMapper.selectById(id);
        return Optional.ofNullable(article)
                .map(a -> {
                    Long commentCount = commentMapper.selectCount(new LambdaQueryWrapper<Comment>().eq(Comment::getArticleId, id));
                    return ArticleVO.convert(a, commentCount);
                })
                .orElseThrow(() -> new IllegalArgumentException("数据异常"));
    }

    @Override
    public Boolean updateArticle(ArticleDTO articleDTO) {
        if (!articleDTO.viewStatus() && articleDTO.recommendStatus()) {
            throw new IllegalArgumentException("不可见文章不可列为推荐");
        }
        if (articleDTO.encoderStatus() && !StringUtils.hasText(articleDTO.password())) {
            throw new IllegalArgumentException("加密时需输入密码");
        }
        Article checkTitle = lambdaQuery()
                .eq(Article::getArticleTitle, articleDTO.articleTitle())
                .ne(Article::getId, articleDTO.id())
                .one();
        if (Objects.nonNull(checkTitle)) {
            throw new IllegalArgumentException("该文章已存在");
        }
        Article article = ArticleDTO.convertDTO(articleDTO);
        boolean update = baseMapper.updateById(article) > 0;
        if (!update) {
            throw new IllegalArgumentException("数据异常");
        }
        //如果是推荐文章，则刷新首页推荐文章缓存
        if (article.getRecommendStatus()) {
            redisUtil.setHomeRedis(BaseConstant.Redis.HOME_RECOMMEND_ARTICLE, this::selectHomeRecommendArticle);
        }
        return true;
    }

    @Override
    public Boolean deleteArticle(Long id) {
        Article article = baseMapper.selectById(id);
        return Optional.ofNullable(article)
                .map(a -> {
                    baseMapper.deleteById(id);
                    //如果是推荐文章，则刷新首页推荐文章缓存
                    if (article.getRecommendStatus()) {
                        redisUtil.setHomeRedis(BaseConstant.Redis.HOME_RECOMMEND_ARTICLE, this::selectHomeRecommendArticle);
                    }
                    return true;
                }).orElseThrow(() -> new IllegalArgumentException("数据异常"));
    }

    @Override
    public Map<Long, List<HomeArticleVO>> getHomeArticle() {
        List<HomeArticleVO> articleList = baseMapper.selectHomeArticle();
        if (CollectionUtils.isEmpty(articleList)) {
            return CollectionUtils.newHashMap(0);
        }
        return articleList.stream()
                .collect(Collectors.groupingBy(HomeArticleVO::sortId));
    }

    @Override
    public List<HomeRecommendArticleVO> getHomeRecommendArticle() {
        return redisUtil.getHomeRedis(BaseConstant.Redis.HOME_RECOMMEND_ARTICLE, this::selectHomeRecommendArticle);
    }

    @Override
    public List<SelectVO> getArticleSelect(String title) {
        List<Article> articleList = selectArticleByTitle(title);
        return SelectVO.convertArticleSelect(articleList);
    }

    @Override
    public ArticleVO getArticleById(Long id) {
        Article article = baseMapper.selectById(id);
        return Optional.ofNullable(article)
                .map(a -> {
                    //每次获取文章详情，该文章热度+1
                    var count = 1;
                    if (Objects.nonNull(a.getViewCount())) {
                        count += a.getViewCount();
                    }
                    lambdaUpdate()
                            .set(Article::getViewCount, count)
                            .eq(Article::getId, a.getId())
                            .update();

                    Long commentCount = commentMapper.selectCount(new LambdaQueryWrapper<Comment>().eq(Comment::getArticleId, id));
                    return ArticleVO.convert(a, commentCount);
                })
                .orElseThrow(() -> new IllegalArgumentException("数据异常"));
    }

    @Override
    public Boolean changeViewStatus(Long id, Boolean status) {
        if (status) {
            lambdaUpdate()
                    .set(Article::getViewStatus, true)
                    .eq(Article::getId, id)
                    .update();
        } else {
            Article article = baseMapper.selectById(id);
            lambdaUpdate()
                    .set(Article::getViewStatus, false)
                    .set(Article::getRecommendStatus, false)
                    .eq(Article::getId, id)
                    .update();
            //如果原本是推荐文章，修改为不可见时，刷新home页推荐文章缓存
            if (article.getRecommendStatus()) {
                redisUtil.setHomeRedis(BaseConstant.Redis.HOME_RECOMMEND_ARTICLE, this::selectHomeRecommendArticle);
            }
        }
        return true;
    }

    @Override
    public Boolean changeCommentStatus(Long id, Boolean status) {
        return lambdaUpdate()
                .set(Article::getCommentStatus, status)
                .eq(Article::getId, id)
                .update();
    }

    @Override
    public Boolean changeRecommendStatus(Long id, Boolean status) {
        if (status) {
            Article article = baseMapper.selectById(id);
            if (!article.getViewStatus()) {
                throw new IllegalArgumentException("不可见文章不可列为推荐");
            }
        }
        lambdaUpdate()
                .set(Article::getRecommendStatus, status)
                .eq(Article::getId, id)
                .update();
        //刷新home页推荐文章缓存
        redisUtil.setHomeRedis(BaseConstant.Redis.HOME_RECOMMEND_ARTICLE, this::selectHomeRecommendArticle);
        return true;
    }

    @Override
    public Boolean checkPassword(Long id, String password) {
        Article article = baseMapper.selectById(id);
        return Optional.ofNullable(article)
                .map(art -> art.getPassword().equals(password))
                .orElseThrow(() -> new IllegalArgumentException("密码错误"));
    }

    /*            私有共用方法                      */

    /**
     * 根据title模糊查询
     *
     * @param title
     * @return
     */
    private List<Article> selectArticleByTitle(String title) {
        return lambdaQuery()
                .like(Article::getArticleTitle, title)
                .list();
    }

    /**
     * 主页推荐文章查询
     *
     * @return
     */
    private List<HomeRecommendArticleVO> selectHomeRecommendArticle() {
        List<Article> articleList = lambdaQuery()
                .eq(Article::getRecommendStatus, true)
                .list();
        return HomeRecommendArticleVO.convertRecommendArticle(articleList);
    }
}
